
#define DUA_CORE

#include "dua_def.h"
#include "dua_lex.h"

static void *
defaultAlloc (void * ud, void *p,
        int tt, size_t osz, size_t nsz){
    if(!nsz){
        free(p);
        return NULL;
    }
    return realloc(p, nsz);
}

static int
initVM(DuaState* D){
    duaInitString(D);
    duaInitLex(D);
    return 0;
}

static inline void
expandStack(DuaState* D) {
    if (D->cap >= 1024) {
        duaRet(D, DUA_ERROR, "stack overload");
    }
    unsigned cap = D->cap * 2;
    size_t osz = sizeof(DuaValue) * D->cap;
    size_t nsz = sizeof(DuaValue) * cap;
    D->slot = duaRelloc(D, D->slot, osz, nsz);
    D->cap = cap;
}


static inline DuaValue*
pushValue(DuaState* D) {
    if (D->top >= D->cap - 2) {
        expandStack(D);
    }
    return &D->slot[D->top++];
}

extern void*
duaMalloc(DuaState* D, size_t sz){
    DuaVM* vm = D->vm;
    void* p = vm->alloc(vm->ud, NULL, 0, 0, sz);
    duaCheckAlloc(D, p);
    return p;
}

extern void*
duaRelloc(DuaState* D, void* p, size_t osz, size_t nsz){
    DuaVM* vm = D->vm;
    p = vm->alloc(vm->ud, p, 0, osz, nsz);
    duaCheckAlloc(D, p);
    return p;
}

extern void
duaFree(DuaState* D, void* p, size_t sz){
    DuaVM* vm = D->vm;
    vm->alloc(vm->ud, p, 0, sz, 0);
}

DUA_API DuaState*
duaNewState(DuaAlloc alloc, void*ud){
    if(!alloc){ alloc = defaultAlloc; }
    DuaVM * vm = alloc(ud, NULL, 0, 0, sizeof(DuaVM));
    if(!vm){ return NULL; }
    vm->ud = ud;
    vm->alloc = alloc;

    DuaState* D = alloc(ud,
            NULL, DUA_TSTACK, 0, sizeof(DuaState));
    if(!D){
        alloc(ud, vm, 0, 0, 0);
        return NULL;
    };
    vm->main = D;
    D->vm = vm;
    D->cap = 8;
    D->top = 0;
    D->parent = NULL;
    D->slot = alloc(ud,
            NULL, 0, 0, sizeof(DuaValue) * D->cap);
    if(!D->slot){
        alloc(ud, vm, 0, 0, 0);
        alloc(ud, D, DUA_TSTACK, 0, 0);
        return NULL;
    };
    unsigned nResult;
    if(duaCallFunc(D, initVM, &nResult)){
        alloc(ud, vm, 0, 0, 0);
        alloc(ud, D, DUA_TSTACK, 0, 0);
        return NULL;
    }
    return D;
}

DUA_API void
duaCheckStack(DuaState* D, unsigned top) {
    while (D->top + top > D->cap - 2) {
        expandStack(D);
    }
}


DUA_API void
duaPushBool(DuaState* D, int b) {
    DuaValue* v = pushValue(D);
    v->type = DUA_TBOOL;
    v->b = b;
}

DUA_API void
duaPushStringLen(DuaState* D, const char* str, size_t len) {
    DuaString* ts = duaNewStringLen(D, str, len);
    DuaValue* v = pushValue(D);
    v->type = DUA_TSTRING;
    v->ts = ts;
}
